This article is based on the TensorFlow Image Classification article where we demonstrate image classification using TensorFlow. The dataset that we use here is a filtered version of Dogs vs Cats dataset from Kaggle.

Dogs vs. Cats Dataset

Downloading Data

In [1]:
import os
import tensorflow as tf
def Get_Data(_URL, Remove = True):
    # The dataset URL
    File = _URL.split('/')[-1]
    Full_Name =  os.path.join(os.getcwd(), File)
    # Download the dataset file from the URL
    path_to_zip = tf.keras.utils.get_file(fname =Full_Name, origin=_URL, extract=True, cache_dir = os.getcwd())
    PATH = os.path.join(os.path.dirname(path_to_zip), 'datasets', File.split('.')[0])
    # Deleting the zip file
    if Remove:
        os.remove(File)
    return PATH
    #-----------------------------------------------------------------
    
_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
PATH = Get_Data(_URL)
Downloading data from https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip
68608000/68606236 [==============================] - 2s 0us/step
68616192/68606236 [==============================] - 2s 0us/step

Dataset Directory Structure

In [2]:
from HD_DirTools import Path_Tree
Path_Tree(PATH)
=======================
cats_and_dogs_filtered:
=======================
└── train:
   └── cats:
       1000 JPG files
       cat.0.jpg, cat.1.jpg, cat.10.jpg, cat.100.jpg, cat.101.jpg, ...
   └── dogs:
       1000 JPG files
       dog.0.jpg, dog.1.jpg, dog.10.jpg, dog.100.jpg, dog.101.jpg, ...
└── validation:
   └── cats:
       500 JPG files
       cat.2000.jpg, cat.2001.jpg, cat.2002.jpg, cat.2003.jpg, cat.2004.jpg, ...
   └── dogs:
       500 JPG files
       dog.2000.jpg, dog.2001.jpg, dog.2002.jpg, dog.2003.jpg, dog.2004.jpg, ...
In [3]:
from HD_DirTools import Data_Info
    
DataFrame_Info, DataDirs = Data_Info(PATH)
display(DataFrame_Info.set_index(['Set' , 'Subset']).T)
Set Train Validation
Subset Cats Dogs Cats Dogs
Size 1000 1000 500 500

Preprocessing

In [4]:
batch_size = 128
epochs = 15
Img_Height = 150
Img_Width = 150

Train Data Image DataGenerator

In [5]:
image_gen_train = tf.keras.preprocessing.image.ImageDataGenerator(
        # Rescaling the tensors from values between 0 and 255 to values between 0 and 1
        rescale=1./255,
        # Applying 45 degrees of rotation randomly
        rotation_range=45,
        # Range for random horizontal shifts.
        width_shift_range=.15,
        # Range for random vertical shifts.
        height_shift_range=.15,
        # applying random horizontal flip augmentation
        horizontal_flip=True,
        # Applying a zoom augmentation to the dataset to zoom images up to 50%
        zoom_range=0.5
        )

print('Train Data:')
# flow_from_directory method load images from the disk
train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size,
                                                     directory=DataDirs['train'],
                                                     shuffle=True,
                                                     target_size=(Img_Height, Img_Width),
                                                     class_mode='binary')
Train Data:
Found 2000 images belonging to 2 classes.
In [6]:
import matplotlib.pyplot as plt
def plotImages(images_arr, s = 3.4, Title = False):
    fig, axes = plt.subplots(1, len(images_arr), figsize=(s* len(images_arr),s))
    axes = axes.flatten()
    for img, ax in zip( images_arr, axes):
        ax.imshow(img)
        ax.axis('off')
    _ = fig.tight_layout()
    _ = fig.subplots_adjust(wspace= 5e-3)
    if Title:
        _ = fig.suptitle(Title, y = 1.05, weight = 'bold', fontsize = 18)

    
sample_images, _ = next(train_data_gen)
plotImages(sample_images[:4], Title = 'Four Random Pictures from the Train Sample')

augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images, Title = 'Original and Augmented Pictures', s = 3)

Validation Data Image DataGenerator

In [7]:
validation_image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)

print('Validation Data:')
val_data_gen = validation_image_generator.flow_from_directory(batch_size = batch_size,
                                                              directory = DataDirs['validation'],
                                                              target_size = (Img_Height, Img_Width),
                                                              class_mode = 'binary')

sample_images, _ = next(val_data_gen)
plotImages(sample_images[:4], Title = 'Four Random Pictures from the Train Sample')
Validation Data:
Found 1000 images belonging to 2 classes.

Modeling

We use Keras Sequential model for creating a model.

In [8]:
model = tf.keras.models.Sequential(name = 'CNN')
model.add(tf.keras.layers.Conv2D(16, 3, padding='same', activation='relu', input_shape=(Img_Height, Img_Width ,3)))
model.add(tf.keras.layers.MaxPooling2D())
# regularization
model.add(tf.keras.layers.Dropout(0.2))

model.add(tf.keras.layers.Conv2D(32, 3, padding='same', activation='relu'))
model.add(tf.keras.layers.MaxPooling2D())
model.add(tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu'))
model.add(tf.keras.layers.MaxPooling2D())
# regularization
model.add(tf.keras.layers.Dropout(0.2))

model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(512, activation='relu'))
model.add(tf.keras.layers.Dense(1))

model.summary()
tf.keras.utils.plot_model(model, show_shapes=True, show_layer_names=False, expand_nested = True, rankdir = 'TB')
Model: "CNN"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 150, 150, 16)      448       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 75, 75, 16)       0         
 )                                                               
                                                                 
 dropout (Dropout)           (None, 75, 75, 16)        0         
                                                                 
 conv2d_1 (Conv2D)           (None, 75, 75, 32)        4640      
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 37, 37, 32)       0         
 2D)                                                             
                                                                 
 conv2d_2 (Conv2D)           (None, 37, 37, 64)        18496     
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 18, 18, 64)       0         
 2D)                                                             
                                                                 
 dropout_1 (Dropout)         (None, 18, 18, 64)        0         
                                                                 
 flatten (Flatten)           (None, 20736)             0         
                                                                 
 dense (Dense)               (None, 512)               10617344  
                                                                 
 dense_1 (Dense)             (None, 1)                 513       
                                                                 
=================================================================
Total params: 10,641,441
Trainable params: 10,641,441
Non-trainable params: 0
_________________________________________________________________
Out[8]:

Compiling and fitting the model

In [9]:
model.compile(optimizer = 'adam', loss = tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics = ['accuracy'])
history = model.fit(train_data_gen,
              steps_per_epoch = DataFrame_Info.loc[DataFrame_Info['Set'] == 'Train','Size'].sum() // batch_size,
              epochs = epochs,
              validation_data = val_data_gen,
              validation_steps = DataFrame_Info.loc[DataFrame_Info['Set'] == 'Validation','Size'].sum() // batch_size)
# clear_output()
Epoch 1/15
15/15 [==============================] - 19s 1s/step - loss: 1.3590 - accuracy: 0.5085 - val_loss: 0.6936 - val_accuracy: 0.5022
Epoch 2/15
15/15 [==============================] - 12s 780ms/step - loss: 0.6932 - accuracy: 0.5021 - val_loss: 0.6931 - val_accuracy: 0.4955
Epoch 3/15
15/15 [==============================] - 12s 809ms/step - loss: 0.6932 - accuracy: 0.4989 - val_loss: 0.6932 - val_accuracy: 0.4855
Epoch 4/15
15/15 [==============================] - 12s 803ms/step - loss: 0.6930 - accuracy: 0.5011 - val_loss: 0.6927 - val_accuracy: 0.4978
Epoch 5/15
15/15 [==============================] - 12s 805ms/step - loss: 0.6930 - accuracy: 0.4979 - val_loss: 0.6914 - val_accuracy: 0.5078
Epoch 6/15
15/15 [==============================] - 12s 807ms/step - loss: 0.6912 - accuracy: 0.4957 - val_loss: 0.6897 - val_accuracy: 0.5056
Epoch 7/15
15/15 [==============================] - 12s 770ms/step - loss: 0.6903 - accuracy: 0.4979 - val_loss: 0.6866 - val_accuracy: 0.5022
Epoch 8/15
15/15 [==============================] - 12s 763ms/step - loss: 0.6885 - accuracy: 0.5005 - val_loss: 0.6750 - val_accuracy: 0.5033
Epoch 9/15
15/15 [==============================] - 12s 792ms/step - loss: 0.6882 - accuracy: 0.5021 - val_loss: 0.6855 - val_accuracy: 0.5056
Epoch 10/15
15/15 [==============================] - 12s 770ms/step - loss: 0.6843 - accuracy: 0.5027 - val_loss: 0.6872 - val_accuracy: 0.4944
Epoch 11/15
15/15 [==============================] - 12s 769ms/step - loss: 0.6790 - accuracy: 0.5123 - val_loss: 0.6956 - val_accuracy: 0.5290
Epoch 12/15
15/15 [==============================] - 12s 787ms/step - loss: 0.6741 - accuracy: 0.5401 - val_loss: 0.6756 - val_accuracy: 0.5234
Epoch 13/15
15/15 [==============================] - 12s 770ms/step - loss: 0.6722 - accuracy: 0.5443 - val_loss: 0.6595 - val_accuracy: 0.5547
Epoch 14/15
15/15 [==============================] - 12s 774ms/step - loss: 0.6559 - accuracy: 0.5716 - val_loss: 0.6576 - val_accuracy: 0.5815
Epoch 15/15
15/15 [==============================] - 12s 787ms/step - loss: 0.6613 - accuracy: 0.5593 - val_loss: 0.6345 - val_accuracy: 0.6027
In [10]:
def Search_List(Key, List): return [s for s in List if Key in s]

Metrics_Names = {'loss':'Loss', 'accuracy':'Accuracy', 'mae':'MAE', 'mse':'MSE', 'recall': 'Recall'}

import pandas as pd
import numpy as np
from HD_DeepLearning import history2Table

Validation_Table = Search_List('val_',history.history.keys()) 
Train_Table = list(set( history.history.keys()) - set(Validation_Table))
Validation_Table = pd.DataFrame(np.array([history.history[x] for x in Validation_Table]).T, columns = Validation_Table)
Train_Table = pd.DataFrame(np.array([history.history[x] for x in Train_Table]).T, columns = Train_Table)
Validation_Table.columns = [x.replace('val_','') for x in Validation_Table.columns]

Train_Table = history2Table(Train_Table, Metrics_Names)
Validation_Table = history2Table(Validation_Table, Metrics_Names)

# Train Set Score
score = model.evaluate(train_data_gen, batch_size = batch_size, verbose = 0)
score = pd.DataFrame(score, index = model.metrics_names).T
score.index = ['Train Set Score']
# Validation Set Score
Temp = model.evaluate(val_data_gen, batch_size = batch_size, verbose = 0) 
Temp = pd.DataFrame(Temp, index = model.metrics_names).T
Temp.index = ['Validation Set Score']
score = pd.concat([score, Temp])
score.rename(columns= Metrics_Names, inplace = True)
score = score.reindex(sorted(score.columns), axis=1)
display(score.style.format(precision=4))
  Accuracy Loss
Train Set Score 0.5745 0.6460
Validation Set Score 0.6050 0.6382
In [11]:
from HD_DeepLearning import Plot_history
PD = dict(row_heights = [0.4, 0.6], lw = 1.5, font_size=12, height = 700, yLim = 1,
          th_line_color = 'Navy', th_fill_color='darkslategray', table_columnwidth = [0.4, 0.4, 0.4, 0.4],
          tc_line_color = 'Navy', tc_fill_color = None, title_x = 0.46, title_y = 0.94, tb_cell_heigh = 20,
          Number_Format = '%.4e')

Plot_history(Train_Table, PD, Title = 'Train Set', Table_Rows = 10, Colors = ['DarkGreen', 'Red'])
Plot_history(Validation_Table, PD, Title = 'Validation Set', Table_Rows = 10, Colors = [ 'DarkGreen', 'Red'])

Here, we only went through a few iterations; however, we need to train the model for more iterations to get acceptable results.

In [12]:
# del downloaded images
import shutil
shutil.rmtree('datasets/cats_and_dogs_filtered')